using System;
using System.Collections.Generic;
using System.IO;
using LitJson;
using UI;
using UnityEditor;
using UnityEngine;

namespace Manager
{
    /// <summary>
    /// UI管理者
    /// </summary>
    public class UIManager
    {
        //加载预制体目录
        private readonly string panelPrefabPath = Application.dataPath + @"/Resources/UIPanelPrefabs";
        //UIJson目录
        private readonly string jsonPath = Application.dataPath + @"/Json/UIJson.json";
        
        //UIPanel列表
        private List<UIPanel> panelList;
        
        //实例面板BasePanel 键值对
        private Dictionary<UIPanelType, BasePanel> panelDict;
        
        //Canvas组件        
        private Transform canvasTransform;
        public Transform CanvasTransform
        {
            get
            {
                if (canvasTransform == null)
                    //通过名称获取Canvas上的Transform，所以不要有同名Canvas
                    canvasTransform = GameObject.Find("Canvas").transform;
                return canvasTransform;
            }
        }

        //活跃的ui面板栈
        private Stack<BasePanel> panelStack;

        
        private static UIManager instance;

        public static UIManager Instance
        {
            get
            {
                if (instance != null) return instance;
                instance = new UIManager();
                
                return instance;
            }
        }

        private UIManager()
        {
            UIPanelInfoSaveInJson();
        }
        
        //读取Json文件并转换为List类型的列表
        private List<UIPanel> ReadJsonFile()
        {
            
            //如果仅新建一个空Json文件，Json转换会返回一个null，也就是后面的list等于null，之后使用list的操作就会报一个空指针错误。
            //如果找不到UIJson文件，则新建一个Json文件并写入“[]”
            if (!File.Exists(jsonPath))
                File.WriteAllText(jsonPath, "[]");
            
            List<UIPanel> list = JsonMapper.ToObject<List<UIPanel>>(File.ReadAllText(jsonPath));

            return list;
        }
        
        //传入Json路径和List类型的UIPanel的列表，可以把该列表内容写入该路径的Json文件
        private void WriteJsonFile(List<UIPanel> list)
        {
            string json = JsonMapper.ToJson(list);
            File.WriteAllText(jsonPath, json);
        }

        //查找type相同的UIPanel
        private UIPanel SearchPanelForType(List<UIPanel> list,UIPanelType type)
        {
            if (list != null)
            {
                foreach (var item in list)
                {
                    if (item.panelType == type)
                        return item;
                }
            }
            return null;
        }
        
        //自动更新Json文件的方法
        private void UIPanelInfoSaveInJson()
        {    	
            panelList = ReadJsonFile();//读取现有json里的UIPanelList

            //读取储存面板prefab的文件夹的信息
            DirectoryInfo folder = new DirectoryInfo(panelPrefabPath);

            //遍历储存面板prefab的文件夹里每一个prefab的名字，并把名字转换为对应的UIPanelType
            //再检查UIPanelType是否存在于List里,若存在List里则更新path,若不存在List里则加上
            foreach (FileInfo file in folder.GetFiles("*.prefab"))
            {
                UIPanelType type = (UIPanelType)Enum.Parse(typeof(UIPanelType), file.Name.Replace(".prefab", ""));
                //这里的path是相对路径，因为加载时使用的是Resources.load
                string path = @"UIPanelPrefab/" + file.Name.Replace(".prefab", "");
                
                //在List里查找type相同的UIPanel
                //SearchPanelForType是一个List的扩展方法，具体见Extension部分
                UIPanel uIPanel = SearchPanelForType(panelList,type);
                if (uIPanel != null)//UIPanel在该List中,更新path值
                {
                    uIPanel.panelPath = path;
                   
                }
                else
                {
                    //新建panel加入list
                    UIPanel panel = new UIPanel
                    {
                        panelType = type,
                        panelPath = path
                    };
                    panelList.Add(panel);
                }
              
            }

            WriteJsonFile(panelList);//把更新后的UIPanelList写入Json

            AssetDatabase.Refresh();//刷新资源
        }

        //实例化面板
        public BasePanel GetPanel(UIPanelType type)
        {
            if (panelDict == null)
                panelDict = new Dictionary<UIPanelType, BasePanel>();
            //【扩展发放】通过type查找字典里对应的BasePanel，若找不到则返回null，具体见Extension部分
            BasePanel panel = panelDict[type];

            //在现有字典里没有找到
            //只能去json里找type对应的prefab的路径并加载
            //再加进字典里以便下次在字典中查找
            if (panel == null)
            {
                
                string path =SearchPanelForType(panelList,type).panelPath;
                if (path == null)
                    throw new Exception("找不到该UIPanelType的Prefab");

                if (Resources.Load(path) == null)
                    throw new Exception("找不到该Path的Prefab");
                //实例化prefab
                GameObject instPanel = GameObject.Instantiate(Resources.Load(path)) as GameObject;
                //把面板设为Canvas的子物体，false表示不保持worldPosition，而是根据Canvas的坐标设定localPosition
                instPanel.transform.SetParent(CanvasTransform, false);

                panelDict.Add(type, instPanel.GetComponent<BasePanel>());

                return instPanel.GetComponent<BasePanel>();
            }

            return panel;
        }
        
        //把指定类型的panel入栈,并显示在场景中
        public void PushPanel(UIPanelType type)
        {
            if (panelStack == null)
                panelStack = new Stack<BasePanel>();

            //判断栈里是否有其他panel,若有,则把原栈顶panel设定其状态为暂停(OnPause)
            if (panelStack.Count > 0)
            {
                BasePanel topPanel = panelStack.Peek();
                topPanel.OnPause();
            }

            BasePanel panel = GetPanel(type);

            //把指定类型的panel入栈并设定其状态为进入场景(OnEnter)
            panelStack.Push(panel);
            panel.OnEnter();
        }

        //把栈顶panel出栈,并从场景中消失
        public void PopPanel()
        {
            if (panelStack == null)
                panelStack = new Stack<BasePanel>();
		
            //检查栈是否为空，若为空则直接退出方法
            if (panelStack.Count <= 0) return;

            //把栈顶panel出栈，并把该panel状态设为退出场景(OnExit)
            BasePanel topPanel = panelStack.Pop();
            topPanel.OnExit();

            //再次检查出栈栈顶Panel后栈是否为空
            //若为空，直接退出方法
            //若不为空，则把新的栈顶Panel状态设为继续(OnResume)
            if (panelStack.Count <= 0) return;

            BasePanel topPanel2 = panelStack.Peek();
            topPanel2.OnResume();
        }

    }
}